// RedJey Graphics User Interface Library for J2ME Platform.
// Copyright (C) 2008 Yevgen Krapiva
//
// This library is free software; you can redistribute
// it and/or modify it under the terms of the
// GNU Lesser General Public License as published
// by the Free Software Foundation; either version 3.0
// of the License, or any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//

package com.incrediblesoft.redjey;

/**
 * @author Yevgen Krapiva<br>
 *         Homepage: http://code.google.com/p/redjey/<br>
 *         Feedback: ykrapiva@gmail.com<br>
 *         Copyright (C) 2008 Yevgen Krapiva<br>
 */

import com.incrediblesoft.redjey.event.MouseEvent;
import com.incrediblesoft.redjey.event.ActionEvent;
import com.incrediblesoft.redjey.event.ActionListener;
import com.incrediblesoft.redjey.event.MouseListener;

import java.util.Hashtable;

public class ScrollBar extends Container implements /*ActionListener, */MouseListener
{
    /**
     * A constant that indicates a horizontal scroll bar.
     */
    public static final int	HORIZONTAL = 0;

    /**
     * A constant that indicates a vertical scroll bar.
     */
    public static final int	VERTICAL   = 1;

    /**
     * The value of the <code>Scrollbar</code>.
     * This property must be greater than or equal to <code>minimum</code>
     * and less than or equal to
     * <code>maximum - visibleAmount</code>
     */
    protected int	value = 0;

    /**
     * The maximum value of the <code>Scrollbar</code>.
     * This value must be greater than the <code>minimum</code>
     * value.<br>
     */
    protected int	maximum = 100;

    /**
     * The minimum value of the <code>Scrollbar</code>.
     * This value must be less than the <code>maximum</code>
     * value.<br>
     */
    protected int	minimum = 0;

    /**
     * The size of the <code>Scrollbar</code>'s bubble.
     * When a scroll bar is used to select a range of values,
     * the visibleAmount represents the size of this range.
     * This is visually indicated by the size of the bubble.
     */
    protected int	visibleAmount = 10;

    /**
     * The <code>Scrollbar</code>'s orientation--being either horizontal
     * or vertical.
     * This value should be specified when the scrollbar is created.<BR>
     * orientation can be either : <code>VERTICAL</code> or
     * <code>HORIZONTAL</code> only.
     */
    protected int	orientation;

    /**
     * The amount by which the scrollbar value will change when going
     * up or down by a line.
     * This value must be greater than zero.
     */
    protected int lineIncrement = 5;

    /**
     * The amount by which the scrollbar value will change when going
     * up or down by a page.
     * This value must be greater than zero.
     */
    protected int pageIncrement = lineIncrement * 4;

    /**
     * Dummy components that help rendering this component
     */
    protected Component topBtn, bottomBtn, caretBtn;

    /**
     * Creates default scroll bar
     * @param orientation Orientation of scroll bar. Can be HORIZONTAL or VERTICAL.
     */
    public ScrollBar(int orientation)
    {
        this(orientation, null);
    }

    /**
     * Creates new scroll bar.
     * @param orientation Orientation of scroll bar. Can be HORIZONTAL or VERTICAL.
     * @param styleClassName Style class name
     */
    public ScrollBar(int orientation, String styleClassName)
    {
        this(orientation, new Button(null), new Button(null), new Button(null), styleClassName);
    }

    /**
     * Creates new scroll bar
     * @param orientation Orientation of scroll bar. Can be HORIZONTAL or VERTICAL.
     * @param button1 Button component to use as top button in vertical bar or left button in horizontal.
     * @param button2 Button component to use as bottom button in vertical bar or right button in horizontal.
     * @param caretBtn Button component to use as position indicator.
     * @param styleClassName Style class name
     */
    public ScrollBar(int orientation, Component button1, Component button2, Component caretBtn, String styleClassName)
    {
        super(styleClassName);

        this.topBtn = button1;
        this.bottomBtn = button2;
        this.caretBtn = caretBtn;
        this.orientation = orientation;

        topBtn.setFocusable(false);
        bottomBtn.setFocusable(false);
        caretBtn.setFocusable(false);

        add(topBtn);
        add(bottomBtn);
        add(caretBtn);

        topBtn.addMouseListener(this);
        bottomBtn.addMouseListener(this);
        //topBtn.addActionListener(this);
        //bottomBtn.addActionListener(this);

        setState(NORMAL);
    }

    public String getName()
    {
        return "ScrollBar";
    }

    protected Dimension calcPreferredSize(int paddingHorizontal, int paddingVertical, CustomFont font, Hashtable attributes)
    {
        Dimension dim_b1 = topBtn.getPreferredSize();
        Dimension dim_b2 = bottomBtn.getPreferredSize();
        Dimension dim_caret = caretBtn.getPreferredSize();
        Dimension dim = new Dimension();

        switch(orientation)
        {
            case VERTICAL:
                dim.width = Math.max(dim.width, dim_b1.width);
                dim.width = Math.max(dim.width, dim_b2.width);
                dim.width = Math.max(dim.width, dim_caret.width);
                dim.height = UIManager.getCanvas().getHeight() / 4;
                break;

            case HORIZONTAL:
                dim.height = Math.max(dim.height, dim_b1.height);
                dim.height = Math.max(dim.height, dim_b2.height);
                dim.height = Math.max(dim.height, dim_caret.height);
                dim.width = UIManager.getCanvas().getWidth() / 4;
                break;
        }

        return dim;
    }

    public void setState(int index)
    {
        super.setState(index);

        if (topBtn != null)
        {
            String className = (String) currentStyle.attributes.get("top-button-class");
            if (className != null) topBtn.setStyleClass(className);
        }
        if (bottomBtn != null)
        {
            String className = (String) currentStyle.attributes.get("bottom-button-class");
            if (className != null) bottomBtn.setStyleClass(className);
        }
        if (caretBtn != null)
        {
            String className = (String) currentStyle.attributes.get("caret-button-class");
            if (className != null) caretBtn.setStyleClass(className);
        }
    }

    public void setValue(int value)
    {
        this.value = value;
        update();
    }

    public int getValue()
    {
        return value;
    }

    public void setMaximum(int maximum)
    {
        this.maximum = maximum;
        invalidate();
    }

    public int getMaximum()
    {
        return maximum;
    }

    public void setVisibleAmount(int visibleAmount)
    {
        this.visibleAmount = visibleAmount;
        invalidate();
    }

    public int getVisibleAmount()
    {
        return visibleAmount;
    }

    public void setLineIncrement(int value)
    {
        this.lineIncrement = value;
    }

    public int getLineIncrement()
    {
        return lineIncrement;
    }

    protected void processMouseEvent(MouseEvent event)
    {
        switch(event.getID())
        {
            case MouseEvent.MOUSE_PRESSED:
                if (orientation == HORIZONTAL)
                {
                    if (event.getX() < caretBtn.x && event.getX() > topBtn.x + topBtn.width)
                    {
                        setValue(value - pageIncrement);
                        fireActionEvent();
                    }
                    else if (event.getX() > caretBtn.x + caretBtn.getWidth() && event.getX() < bottomBtn.x)
                    {
                        setValue(value + pageIncrement);
                        fireActionEvent();
                    }
                }
                else if (orientation == VERTICAL)
                {
                    if (event.getY() < caretBtn.y && event.getY() > topBtn.y + topBtn.height)
                    {
                        setValue(value - pageIncrement);
                        fireActionEvent();
                    }
                    else if (event.getY() > caretBtn.y + caretBtn.getHeight() && event.getY() < bottomBtn.y)
                    {
                        setValue(value + pageIncrement);
                        fireActionEvent();
                    }
                }
                break;
        }

        super.processMouseEvent(event);
    }

    protected void validateComponent()
    {
        Dimension dim_b1 = topBtn.getPreferredSize();
        Dimension dim_b2 = bottomBtn.getPreferredSize();

        switch (orientation)
        {
            case VERTICAL:
                topBtn.setSize(clientSize.width, dim_b1.height);
                bottomBtn.setSize(clientSize.width, dim_b2.height);
                topBtn.setLocation(insets[0], insets[2]);
                bottomBtn.setLocation(insets[0], insets[2] + clientSize.height - bottomBtn.getHeight());
                break;

            case HORIZONTAL:
                topBtn.setSize(dim_b1.width, clientSize.height);
                bottomBtn.setSize(dim_b2.width, clientSize.height);
                topBtn.setLocation(insets[0], insets[2]);
                bottomBtn.setLocation(insets[0] + clientSize.width - topBtn.getWidth(), insets[2]);
                break;
        }
        
        update();
    }

    /**
     * Updates caret position and size
     */
    public void update()
    {
        value = Math.min(value, maximum - visibleAmount);
        value = Math.max(value, minimum);
        int spaceLeft = 0;
        
        if (orientation == VERTICAL)
            spaceLeft = clientSize.height - topBtn.getHeight() - bottomBtn.getHeight();
        else if (orientation == HORIZONTAL)
            spaceLeft = clientSize.width - topBtn.getWidth() - bottomBtn.getWidth();

        int scrollAmount = maximum - visibleAmount;
        int a = spaceLeft * visibleAmount / (scrollAmount + visibleAmount);
        a = Math.min(a, spaceLeft);
        int b = (spaceLeft - a) * value / Math.max(scrollAmount, 1);

        switch (orientation)
        {
            case VERTICAL:
                if (caretBtn.width != clientSize.width || caretBtn.height != a)
                    caretBtn.setSize(clientSize.width,  a);
                caretBtn.setLocation(insets[0], insets[2] + b + topBtn.getHeight());
                break;

            case HORIZONTAL:
                if (caretBtn.width != a || caretBtn.height != clientSize.height)
                    caretBtn.setSize(a, clientSize.height);
                caretBtn.setLocation(insets[0] + b + topBtn.getWidth(), insets[2]);
                break;
        }
    }

    /*public void actionPerformed(ActionEvent event)
    {
        Component source = event.getSource();

        if (source == topBtn)
        {
            setValue(value - lineIncrement);
            fireActionEvent();
        }
        else if (source == bottomBtn)
        {
            setValue(value + lineIncrement);
            fireActionEvent();
        }
    }*/

    public void mousePressed(MouseEvent event)
    {
        Component source = event.getSource();

        if (source == topBtn)
        {
            setValue(value - lineIncrement);
            fireActionEvent();
        }
        else if (source == bottomBtn)
        {
            setValue(value + lineIncrement);
            fireActionEvent();
        }
    }

    public void mouseReleased(MouseEvent event)
    {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    public void mouseClicked(MouseEvent event)
    {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    public void mouseScrolled(MouseEvent event)
    {
        //To change body of implemented methods use File | Settings | File Templates.
    }
}
